A composer is responsible to initialize a component (or a component of tree) when ZK loader is composing a component. It is the controller in the MVC pattern, while the component is the view,which separate the code from the user interfaces.
Composers can be created with the create-composer target. For example try running the following command from the root of a Grails project:
grails create-composer window
The command will result in the creation of a composer at the location grails-app/composers/WindowComposer.groovy:
class WindowComposer {
def afterCompose = {Component comp ->
// initialize components here
}
}
WindowComposer by default provide a afterCompose
closure to initialize components.
The create-composer command is merely for convenience and you can just as easily create composers using your favorite text editor or IDE
Each component Taglib has a apply
attribute,so you can place the composer here
<z:window id="myWindowComposer" apply="package.WindowComposer">
…
</z:window>
In the WindowComposer side
def afterCompose = {Component comp ->
assert comp.id=="myWindowComposer"
assert (comp instanceof org.zkoss.zul.Window)
}
If apply a composer
to a Component that implement a IdSpace
Interface,such as org.zkoss.zul.Window. The Component's children can be auto-wired to composer's fieldsThe following is an example. The onChange event received by Textbox mytextbox
will be forwarded to target Window mywin
as a new target event onChange_mytextbox and the Textbox component with id name "mytextbox" and Label with id name mylabel
are injected into the "mytextbox" and "mylabel" fields respectively(so you can use mytextbox and mylabel variable directly in onChange_mytextbox without problem).Composer
class MyComposer{
Textbox mytextbox
Window self //embeded object, the supervised window "mywin"
Page page //the ZK page
Label mylabel def afterCompose = {Component comp ->
assert mytextbox.id=="mytextbox"
assert mylabel.id=="mylabel"
} def onChange_mytextbox(Event event) {
mylabel.setValue("You just entered: "+ mytextbox.getValue())
}
}
View
<z:window id="mywin" apply="MyComposer">
<z:textbox id="mytextbox"/>
<z:label id="mylabel"/>
</z:window>
Actions can be redirected using the redirect method present in all composersThe parameters of redirect
is same as controller's redirect
Composer
class MyComposer{
Button mybutton def afterCompose = {Component comp -> } def onClick_mybutton(Event event) {
redirect(controller: 'demo', action: 'index', id: 1)
}
}
following code same as onClick_button
abovedef afterCompose = {Component comp ->
mybutton.addEventListener('onClick'){
redirect(controller: 'demo', action: 'index', id: 1)
}
}
View
<z:window id="mywin" apply="MyComposer">
<z:button label="mybutton"/>
</z:window>
To uses Grails' underlying data binding capability,zkui
injection a getParams
method to Component.Domain Class
class Person {
String firstName
String lastName
String fullName
static constraints = {
}
}
View
<z:window id="mywin" apply="MyComposer">
<z:textbox name="firstName"/>
<z:textbox name="lastName"/>
<z:textbox name="fullName"/>
<z:button label="submit"/>
</z:window>
Composer
class MyComposer{
Button submit
def afterCompose = {Component mywin ->
submit.addEventListener('onClick'){
def person=new Person(mywin.params)
…
}
}
}
A bindData
method same as in controller
also provide to Composerdef p = new Person()
bindData(p, mywin.params)
When use grails create-composer create a composer,a unit class that is a sub-class of ComposerUnitTestCase also createdIt provides a mockComposer
methods for mocking zkui's Selector,Builder and so on.mockComposer(MyComposer)
def myComposer=new MyComposer
...
zkui
injection a renderErrors
method to Component.If you have Domain class
class Book{
String author
String title
}
In View
<z:window id="formWindow" title="demo" apply="your.Composer">
<z:textbox name="author"/>
<z:textbox name="title"/>
…
</z:window>
Then in your.Composer
use renderErrorsif (!book.save()) {
formWindow.renderErrors(bean: book)
}
In addition,you can also use grails's traditional renderErrors
In your.Composer
if (!book.save()) {
flash.book = book
redirect(controller: "book", action: "edit", id: book.id)
}
In your view
<g:hasErrors bean="${flash.book}">
<div class="errors">
<g:renderErrors bean="${flash.book}" as="list" />
</div>
</g:hasErrors>
Data binding is a mechanism that automates the data-copy plumbing code (CRUD) between UI components and the data source. Application developers only have to tell the data binding manager about the associations between UI components and the data source. Then, the data -binding manager will do all the loading (loading data from the data source to UI components) and saving (saving data from UI component into the data source) jobs automatically.Activates Data Binding Manager
<z:window apply="org.zkoss.demo.MyComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
</z:window>
For more information, please refer to the relative blog post Databinding ComposerAssociate UI Components with Data Source
After activating the data-binding manager, you have to define the required UI objects and then associate them with the data source.In the gsp view:
<z:window id="win" apply="test.TestComposer,org.zkoss.zkplus.databind.AnnotateDataBindingComposer">
<z:textbox value="@{win#composer.username}"/>
<z:/window>
In the test.TestComposer:
class TestComposer{ def username="test" def afterCompose = {Component comp ->
…
}
}
See more ZK Developer's Reference/Data Binding